/**
 * @file	
 * @author	chipsea
 * @brief	
 * @version	0.1
 * @date	2020-11-30
 * @copyright Copyright (c) 2020, CHIPSEA Co., Ltd.
 * @note
 */

/**************************************************************************************************
  Filename:       gapbondmgr.c
  Revised:         
  Revision:        

  Description:    GAP peripheral profile manages bonded connections


 
**************************************************************************************************/
#include "sdk_config.h"
#include "bcomdef.h"
#if ( HOST_CONFIG & ( CENTRAL_CFG | PERIPHERAL_CFG ) )

/*********************************************************************
 * INCLUDES
 */
#include "OSAL.h"
#include "osal_snv.h"
#include "gap.h"
#include "linkdb.h"
#include "gatt.h"
#include "gatt_uuid.h"
#include "hci.h"
#include "gattservapp.h"
#include "gapgattserver.h"
#include "gapbondmgr.h"

// temp set
// #define osal_snv_write( a, b, c )      (1)
// #define osal_snv_read( a, b, c )       (1)
// #define osal_snv_compact( a )          (1)
#define BOND_LOG(...)
/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * CONSTANTS
 */
// Task event types
#define GAP_BOND_SYNC_CC_EVT                            0x0001 // Sync char config
#define GAP_BOND_SAVE_REC_EVT                           0x0002 // Save bond record in NV

// Once NV usage reaches this percentage threshold, NV compaction gets triggered.
#define NV_COMPACT_THRESHOLD                            80

// Bonded State Flags
#define GAP_BONDED_STATE_AUTHENTICATED                  0x0001
#define GAP_BONDED_STATE_SERVICE_CHANGED                0x0002

/**
 * GAP Bond Manager NV layout
 *
 * The NV definitions:
 *     BLE_NVID_GAP_BOND_START - starting NV ID
 *     GAP_BONDINGS_MAX - Maximum number of bonding allowed (10 is max for number of NV IDs allocated in bcomdef.h).
 *
 * A single bonding entry consists of 6 components (NV items):
 *     Bond Record - defined as gapBondRec_t and uses GAP_BOND_REC_ID_OFFSET for an NV ID
 *     local LTK Info - defined as gapBondLTK_t and uses GAP_BOND_LOCAL_LTK_OFFSET for an NV ID
 *     device LTK Info - defined as gapBondLTK_t and uses GAP_BOND_DEV_LTK_OFFSET for an NV ID
 *     device IRK - defined as "uint8 devIRK[KEYLEN]" and uses GAP_BOND_DEV_IRK_OFFSET for an NV ID
 *     device CSRK - defined as "uint8 devCSRK[KEYLEN]" and uses GAP_BOND_DEV_CSRK_OFFSET for an NV ID
 *     device Sign Counter - defined as a uint32 and uses GAP_BOND_DEV_SIGN_COUNTER_OFFSET for an NV ID
 *
 * When the device is initialized for the first time, all (GAP_BONDINGS_MAX) NV items are created and
 * initialized to all 0xFF's. A bonding record of all 0xFF's indicates that the bonding record is empty
 * and free to use.
 *
 * The calculation for each bonding records NV IDs:
 *    mainRecordNvID = ((bondIdx * GAP_BOND_REC_IDS) + BLE_NVID_GAP_BOND_START)
 *    localLTKNvID = (((bondIdx * GAP_BOND_REC_IDS) + GAP_BOND_LOCAL_LTK_OFFSET) + BLE_NVID_GAP_BOND_START)
 *
 */
#define GAP_BOND_REC_ID_OFFSET              0 //!< NV ID for the main bonding record
#define GAP_BOND_LOCAL_LTK_OFFSET           1 //!< NV ID for the bonding record's local LTK information
#define GAP_BOND_DEV_LTK_OFFSET             2 //!< NV ID for the bonding records' device LTK information
#define GAP_BOND_DEV_IRK_OFFSET             3 //!< NV ID for the bonding records' device IRK
#define GAP_BOND_DEV_CSRK_OFFSET            4 //!< NV ID for the bonding records' device CSRK
#define GAP_BOND_DEV_SIGN_COUNTER_OFFSET    5 //!< NV ID for the bonding records' device Sign Counter

#define GAP_BOND_REC_IDS                    6

// Macros to calculate the index/offset in to NV space
#define calcNvID(Idx, offset)               (((((Idx) * GAP_BOND_REC_IDS) + (offset))) + BLE_NVID_GAP_BOND_START)
#define mainRecordNvID(bondIdx)             (calcNvID((bondIdx), GAP_BOND_REC_ID_OFFSET))
#define localLTKNvID(bondIdx)               (calcNvID((bondIdx), GAP_BOND_LOCAL_LTK_OFFSET))
#define devLTKNvID(bondIdx)                 (calcNvID((bondIdx), GAP_BOND_DEV_LTK_OFFSET))
#define devIRKNvID(bondIdx)                 (calcNvID((bondIdx), GAP_BOND_DEV_IRK_OFFSET))
#define devCSRKNvID(bondIdx)                (calcNvID((bondIdx), GAP_BOND_DEV_CSRK_OFFSET))
#define devSignCounterNvID(bondIdx)         (calcNvID((bondIdx), GAP_BOND_DEV_SIGN_COUNTER_OFFSET))

// Macros to calculate the GATT index/offset in to NV space
#define gattCfgNvID(Idx)                    ((Idx) + BLE_NVID_GATT_CFG_START)

// Key Size Limits
#define MIN_ENC_KEYSIZE                     7  //!< Minimum number of bytes for the encryption key
#define MAX_ENC_KEYSIZE                     16 //!< Maximum number of bytes for the encryption key

/*********************************************************************
 * TYPEDEFS
 */

// Structure of NV data for the connected device's encryption information
typedef struct
{
  uint8   LTK[KEYLEN];              // Long Term Key (LTK)
  uint16  div;  //lint -e754        // LTK eDiv
  uint8   rand[B_RANDOM_NUM_SIZE];  // LTK random number
  uint8   keySize;                  // LTK key size
} gapBondLTK_t;

// Structure of NV data for the connected device's address information
typedef struct
{
  uint8   publicAddr[B_ADDR_LEN];     // Master's address
  uint8   reconnectAddr[B_ADDR_LEN];  // Privacy Reconnection Address
  uint16  stateFlags;                 // State flags: SM_AUTH_STATE_AUTHENTICATED & SM_AUTH_STATE_BONDING
} gapBondRec_t;

// Structure of NV data for the connected device's characteristic configuration
typedef struct
{
  uint16 attrHandle;  // attribute handle
  uint8  value;       // attribute value for this device
} gapBondCharCfg_t;

/*********************************************************************
 * GLOBAL VARIABLES
 */

/*********************************************************************
 * EXTERNAL VARIABLES
 */

/*********************************************************************
 * EXTERNAL FUNCTIONS
 */

/*********************************************************************
 * LOCAL VARIABLES
 */
static uint8 gapBondMgr_TaskID = INVALID_TASK_ID;   // Task ID for internal task/event processing

// GAPBonding Parameters
static uint8 gapBond_PairingMode[MAX_NUM_LL_CONN] = {GAPBOND_PAIRING_MODE_WAIT_FOR_REQ};
static uint16 gapBond_InitiateWait = 1000;  // Default to 1 second
static uint8 gapBond_MITM = FALSE;
static uint8 gapBond_IOCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
static uint8 gapBond_OOBDataFlag = FALSE;
static uint8 gapBond_OOBData[KEYLEN] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static uint8 gapBond_Bonding = FALSE;
// 2020-04-22 support secure connection pairing
static uint8 gapBond_SC = FALSE;
static uint8 gapBond_keyPress = FALSE;
static uint8 gapBond_CT2 = FALSE;
static uint8 gapBond_AutoFail = FALSE;
static uint8 gapBond_AutoFailReason = SMP_PAIRING_FAILED_NOT_SUPPORTED;
static uint8 gapBond_KeyDistList =
(
  GAPBOND_KEYDIST_SENCKEY     // sEncKey enabled, to send the encryption key
   | GAPBOND_KEYDIST_SIDKEY   // sIdKey enabled, to send the IRK, and BD_ADDR
   | GAPBOND_KEYDIST_SSIGN    // sSign enabled, to send the CSRK
   | GAPBOND_KEYDIST_MENCKEY  // mEncKey enabled, to get the master's encryption key
   | GAPBOND_KEYDIST_MIDKEY   // mIdKey enabled, to get the master's IRK and BD_ADDR
   | GAPBOND_KEYDIST_MSIGN    // mSign enabled, to get the master's CSRK
);
static uint32 gapBond_Passcode = 0;
static uint8  gapBond_KeySize = MAX_ENC_KEYSIZE;
#if (GAP_BOND_MGR_INDEX_REPLACE)
    static uint8 bondReplaceCnt=0;
#endif
#if ( HOST_CONFIG & CENTRAL_CFG )
static uint8  gapBond_BondFailOption = GAPBOND_FAIL_TERMINATE_LINK;
#endif

static const gapBondCBs_t *pGapBondCB = NULL;

// Local RAM shadowed bond records
static gapBondRec_t bonds[GAP_BONDINGS_MAX] = {0};

static uint8 autoSyncWhiteList = FALSE;

static uint8 eraseAllBonds = FALSE;

static uint8 bondsToDelete[GAP_BONDINGS_MAX] = {FALSE};

// Globals used for saving bond record and CCC values in NV
static uint8 bondIdx = GAP_BONDINGS_MAX;
static gapAuthCompleteEvent_t *pAuthEvt[MAX_NUM_LL_CONN] = {NULL};

/*********************************************************************
 * LOCAL FUNCTIONS
 */
static uint8 gapBondMgrChangeState( uint8 idx, uint16 state, uint8 set );
static uint8 gapBondMgrUpdateCharCfg( uint8 idx, uint16 attrHandle, uint16 value );
static gapBondCharCfg_t *gapBondMgrFindCharCfgItem( uint16 attrHandle,
                                                    gapBondCharCfg_t *charCfgTbl );
static void gapBondMgrInvertCharCfgItem( gapBondCharCfg_t *charCfgTbl );
static uint8 gapBondMgrAddBond( gapBondRec_t *pBondRec, gapAuthCompleteEvent_t *pPkt );
static uint8 gapBondMgrGetStateFlags( uint8 idx );
static bStatus_t gapBondMgrGetPublicAddr( uint8 idx, uint8 *pAddr );
static uint8 gapBondMgrFindReconnectAddr( uint8 *pReconnectAddr );
static uint8 gapBondMgrFindAddr( uint8 *pDevAddr );
static uint8 gapBondMgrResolvePrivateAddr( uint8 *pAddr );
static void gapBondMgrReadBonds( void );
static uint8 gapBondMgrFindEmpty( void );
static uint8 gapBondMgrBondTotal( void );
static bStatus_t gapBondMgrEraseAllBondings( void );
static bStatus_t gapBondMgrEraseBonding( uint8 idx );
static uint8 gapBondMgr_ProcessOSALMsg( osal_event_hdr_t *pMsg );
static void gapBondMgrSendServiceChange( linkDBItem_t *pLinkItem );
static void gapBondMgr_ProcessGATTMsg( gattMsgEvent_t *pMsg );
static void gapBondMgr_ProcessGATTServMsg( gattEventHdr_t *pMsg );
static void gapBondSetupPrivFlag( void );
static void gapBondMgrBondReq( uint16 connHandle, uint8 idx, uint8 stateFlags,
                               uint8 role, uint8 startEncryption );
static void gapBondMgrAuthenticate( uint16 connHandle, uint8 addrType,
                                    gapPairingReq_t *pPairReq );
static void gapBondMgr_SyncWhiteList( void );
static uint8 gapBondMgr_SyncCharCfg( uint16 connHandle );
static void gapBondFreeAuthEvt( uint16 connHandle );
static void gapBondRecvEvt(uint16 connHandle,gapBondRec_t* pBondRec, gapAuthCompleteEvent_t* pPkt );

#if ( HOST_CONFIG & PERIPHERAL_CFG )
static void gapBondMgrSlaveSecurityReq( uint16 connHandle );
#endif

/*********************************************************************
 * NETWORK LAYER CALLBACKS
 */

/*********************************************************************
 * PUBLIC FUNCTIONS
 */

static void gapBondRecvEvt(uint16 connHandle, gapBondRec_t* pBondRec, gapAuthCompleteEvent_t* pPkt )
{
    if ( gapBondMgrAddBond( pBondRec, pPkt ) )
    {
        // Update NV to have same CCC values as GATT database
        // Note: pAuthEvt is a global variable used for deferring the storage
        if ( gapBondMgr_SyncCharCfg( pAuthEvt[connHandle]->connectionHandle ) )
        {
            if ( pGapBondCB && pGapBondCB->pairStateCB )
            {
                // Assume SUCCESS since we got this far.
                pGapBondCB->pairStateCB( pAuthEvt[connHandle]->connectionHandle, GAPBOND_PAIRING_STATE_COMPLETE, SUCCESS );
            }
            // We're done storing bond record and CCC values in NV
            gapBondFreeAuthEvt(connHandle);
        }
    }
}

/*********************************************************************
 * @brief   Set a GAP Bond Manager parameter.
 *
 * Public function defined in gapbondmgr.h.
 */
bStatus_t GAPBondMgr_SetParameter( uint16 param, uint8 len, void *pValue )
{
  bStatus_t ret = SUCCESS;  // return value

  switch ( param )
  {
    case GAPBOND_PAIRING_MODE:
      if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= GAPBOND_PAIRING_MODE_INITIATE) )
      {
        // gapBond_PairingMode = *((uint8*)pValue);
        osal_memset(gapBond_PairingMode,*((uint8*)pValue),sizeof(gapBond_PairingMode));
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case GAPBOND_INITIATE_WAIT:
      if ( len == sizeof ( uint16 ) )
      {
        gapBond_InitiateWait = *((uint16*)pValue);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case GAPBOND_MITM_PROTECTION:
      if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= TRUE) )
      {
        gapBond_MITM = *((uint8*)pValue);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case GAPBOND_IO_CAPABILITIES:
      if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= GAPBOND_IO_CAP_KEYBOARD_DISPLAY) )
      {
        gapBond_IOCap = *((uint8*)pValue);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case GAPBOND_OOB_ENABLED:
      if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= TRUE) )
      {
        gapBond_OOBDataFlag = *((uint8*)pValue);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case GAPBOND_OOB_DATA:
      if ( len == KEYLEN )
      {
        VOID osal_memcpy( gapBond_OOBData, pValue, KEYLEN ) ;
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case GAPBOND_BONDING_ENABLED:
      if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= TRUE) )
      {
        gapBond_Bonding = *((uint8*)pValue);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case GAPBOND_KEY_DIST_LIST:
      if ( len == sizeof ( uint8 ) )
      {
        gapBond_KeyDistList = *((uint8*)pValue);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case GAPBOND_DEFAULT_PASSCODE:
      if ( (len == sizeof ( uint32 ))
          && (*((uint32*)pValue) <= GAP_PASSCODE_MAX) )
      {
        gapBond_Passcode = *((uint32*)pValue);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case GAPBOND_ERASE_ALLBONDS:
      if ( len == 0 )
      {
        // Make sure there's no active connection
        if ( GAP_NumActiveConnections() == 0 )
        {
          // Erase all bonding records
          VOID gapBondMgrEraseAllBondings();

          // See if NV needs a compaction
          VOID osal_snv_compact( NV_COMPACT_THRESHOLD );

          // Make sure Bond RAM Shadow is up-to-date
          gapBondMgrReadBonds();
        }
        else
        {
          eraseAllBonds = TRUE;
        }
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case GAPBOND_ERASE_SINGLEBOND:
      if ( len == (1 + B_ADDR_LEN) )
      {
        uint8 idx;
        uint8 devAddr[B_ADDR_LEN];

        // Reverse bytes
        VOID osal_revmemcpy( devAddr, (uint8 *)pValue+1, B_ADDR_LEN );
        
        // Resolve address and find index
        idx = GAPBondMgr_ResolveAddr( *((uint8 *)pValue), devAddr, NULL );
        if ( idx < GAP_BONDINGS_MAX )
        {
          // Make sure there's no active connection
          if ( GAP_NumActiveConnections() == 0 )
          {
            // Erase bond
            gapBondMgrEraseBonding( idx );
            
            // See if NV needs a compaction
            VOID osal_snv_compact( NV_COMPACT_THRESHOLD );
            
            // Make sure Bond RAM Shadow is up-to-date
            gapBondMgrReadBonds();
          }
          else
          {
            // Mark entry to be deleted when disconnected
            bondsToDelete[idx] = TRUE;
          }
        }
        else
        {
          ret = INVALIDPARAMETER;
        }
      }
      else
      {
        // Parameter is not the correct length
        ret = bleInvalidRange;
      }
      break;
      
    case GAPBOND_AUTO_FAIL_PAIRING:
      if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= TRUE) )
      {
        gapBond_AutoFail = *((uint8*)pValue);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case GAPBOND_AUTO_FAIL_REASON:
      if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= SMP_PAIRING_FAILED_REPEATED_ATTEMPTS) )
      {
        gapBond_AutoFailReason = *((uint8*)pValue);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case GAPBOND_KEYSIZE:
      if ( (len == sizeof ( uint8 ))
          && ((*((uint8*)pValue) >= MIN_ENC_KEYSIZE) && (*((uint8*)pValue) <= MAX_ENC_KEYSIZE)) )
      {
        gapBond_KeySize = *((uint8*)pValue);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case GAPBOND_AUTO_SYNC_WL:
      if ( len == sizeof( uint8 ) )
      {
        uint8 oldVal = autoSyncWhiteList;

        autoSyncWhiteList = *((uint8 *)pValue);

        // only call if parameter changes from FALSE to TRUE
        if ( ( oldVal == FALSE ) && ( autoSyncWhiteList == TRUE ) )
        {
          // make sure bond is updated from NV
          gapBondMgrReadBonds();
        }
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

#if ( HOST_CONFIG & CENTRAL_CFG )
    case GAPBOND_BOND_FAIL_ACTION:
      if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= GAPBOND_FAIL_TERMINATE_ERASE_BONDS) )
      {
        gapBond_BondFailOption = *((uint8*)pValue);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;
#endif

    default:
      // The param value isn't part of this profile, try the GAP.
      if ( (param < TGAP_PARAMID_MAX) && (len == sizeof ( uint16 )) )
      {
        ret = GAP_SetParamValue( param, *((uint16*)pValue) );
      }
      else
      {
        ret = INVALIDPARAMETER;
      }
      break;
  }

  return ( ret );
}

/*********************************************************************
 * @brief   Get a GAP Bond Manager parameter.
 *
 * Public function defined in gapbondmgr.h.
 */
bStatus_t GAPBondMgr_GetParameter( uint16 param, void *pValue )
{
  bStatus_t ret = SUCCESS;  // return value

  switch ( param )
  {
    case GAPBOND_PAIRING_MODE:
      // *((uint8*)pValue) = gapBond_PairingMode;
      break;

    case GAPBOND_INITIATE_WAIT:
      *((uint16*)pValue) = gapBond_InitiateWait;
      break;

    case GAPBOND_MITM_PROTECTION:
      *((uint8*)pValue) = gapBond_MITM;
      break;

    case GAPBOND_IO_CAPABILITIES:
      *((uint8*)pValue) = gapBond_IOCap;
      break;

    case GAPBOND_OOB_ENABLED:
      *((uint8*)pValue) = gapBond_OOBDataFlag;
      break;

    case GAPBOND_OOB_DATA:
      VOID osal_memcpy( pValue, gapBond_OOBData, KEYLEN ) ;
      break;

    case GAPBOND_BONDING_ENABLED:
      *((uint8*)pValue) = gapBond_Bonding;
      break;

    case GAPBOND_KEY_DIST_LIST:
      *((uint8*)pValue) = gapBond_KeyDistList;
      break;

    case GAPBOND_DEFAULT_PASSCODE:
      *((uint32*)pValue) = gapBond_Passcode;
      break;

    case GAPBOND_AUTO_FAIL_PAIRING:
      *((uint8*)pValue) = gapBond_AutoFail;
      break;

    case GAPBOND_AUTO_FAIL_REASON:
      *((uint8*)pValue) = gapBond_AutoFailReason;
      break;

    case GAPBOND_KEYSIZE:
      *((uint8*)pValue) = gapBond_KeySize;
      break;

    case GAPBOND_AUTO_SYNC_WL:
      *((uint8*)pValue) = autoSyncWhiteList;
      break;

    case GAPBOND_BOND_COUNT:
      *((uint8*)pValue) = gapBondMgrBondTotal();
      break;

    default:
      // The param value isn't part of this profile, try the GAP.
      if ( param < TGAP_PARAMID_MAX )
      {
        *((uint16*)pValue) = GAP_GetParamValue( param );
      }
      else
      {
        ret = INVALIDPARAMETER;
      }
      break;
  }

  return ( ret );
}

/*********************************************************************
 * @brief   Notify the Bond Manager that a connection has been made.
 *
 * Public function defined in gapbondmgr.h.
 */
bStatus_t GAPBondMgr_LinkEst( uint8 addrType, uint8 *pDevAddr, uint16 connHandle, uint8 role )
{
  uint8 idx;                          // NV Index
  uint8 publicAddr[B_ADDR_LEN]        // Place to put the public address
      = {0, 0, 0, 0, 0, 0};

  idx = GAPBondMgr_ResolveAddr( addrType, pDevAddr, publicAddr );
  if ( idx < GAP_BONDINGS_MAX )
  {
    uint8 stateFlags = gapBondMgrGetStateFlags( idx );
    smSigningInfo_t signingInfo;
    gapBondCharCfg_t charCfg[GAP_CHAR_CFG_MAX]; // Space to read a char cfg record from NV

    // On peripheral, load the key information for the bonding
    // On central and initiaiting security, load key to initiate encyption
    gapBondMgrBondReq( connHandle, idx, stateFlags, role,
                       ((role == GAP_PROFILE_CENTRAL ) ? TRUE : FALSE) );

    // Load the Signing Key
    VOID osal_memset( &signingInfo, 0, sizeof ( smSigningInfo_t ) );
    if ( osal_snv_read( devCSRKNvID(idx), KEYLEN, signingInfo.srk ) == SUCCESS )
    {
      if ( osal_isbufset( signingInfo.srk, 0xFF, KEYLEN ) == FALSE )
      {
        // Load the signing information for this connection
        VOID osal_snv_read( devSignCounterNvID(idx), sizeof ( uint32 ), &(signingInfo.signCounter) );
        VOID GAP_Signable( connHandle,
                          ((stateFlags & GAP_BONDED_STATE_AUTHENTICATED) ? TRUE : FALSE),
                          &signingInfo );
      }
    }

    // Load the characteristic configuration
    if ( osal_snv_read( gattCfgNvID(idx), sizeof ( charCfg ), charCfg ) == SUCCESS )
    {
      gapBondMgrInvertCharCfgItem( charCfg );

      for ( uint8 i = 0; i < GAP_CHAR_CFG_MAX; i++ )
      {
        gapBondCharCfg_t *pItem = &(charCfg[i]);

        // Apply the characteristic configuration for this connection
        if ( pItem->attrHandle != GATT_INVALID_HANDLE )
        {
          VOID GATTServApp_UpdateCharCfg( connHandle, pItem->attrHandle,
                                          (uint16)(pItem->value) );
        }
      }
    }

    // Has there been a service change?
    if ( stateFlags & GAP_BONDED_STATE_SERVICE_CHANGED )
    {
      VOID GATTServApp_SendServiceChangedInd( connHandle, gapBondMgr_TaskID );
    }
  }
#if ( HOST_CONFIG & CENTRAL_CFG )
  else if ( role == GAP_PROFILE_CENTRAL &&
            gapBond_PairingMode[connHandle] == GAPBOND_PAIRING_MODE_INITIATE )
  {
    // If Central and initiating and not bonded, then initiate pairing
    gapBondMgrAuthenticate( connHandle, addrType, NULL );

    // Call app state callback
    if ( pGapBondCB && pGapBondCB->pairStateCB )
    {
      pGapBondCB->pairStateCB( connHandle, GAPBOND_PAIRING_STATE_STARTED, SUCCESS );
    }
  }
#endif

#if ( HOST_CONFIG & PERIPHERAL_CFG )
  // If Peripheral and initiating, send a slave security request to
  // initiate either pairing or encryption
  if ( role == GAP_PROFILE_PERIPHERAL &&
       gapBond_PairingMode[connHandle] == GAPBOND_PAIRING_MODE_INITIATE )
  {
    gapBondMgrSlaveSecurityReq( connHandle );
  }
#endif

  return ( SUCCESS );
}

/*********************************************************************
 * @brief   Resolve an address from bonding information.
 *
 * Public function defined in gapbondmgr.h.
 */
uint8 GAPBondMgr_ResolveAddr( uint8 addrType, uint8 *pDevAddr, uint8 *pResolvedAddr )
{
  uint8 idx = GAP_BONDINGS_MAX;

  switch ( addrType )
  {
    case ADDRTYPE_PUBLIC:
    case ADDRTYPE_STATIC:
      idx = gapBondMgrFindAddr( pDevAddr );
      if ( (idx < GAP_BONDINGS_MAX) && (pResolvedAddr) )
      {
        VOID osal_memcpy( pResolvedAddr, pDevAddr, B_ADDR_LEN );
      }
      break;

    case ADDRTYPE_PRIVATE_NONRESOLVE:
      // This could be a reconnection address
      idx = gapBondMgrFindReconnectAddr( pDevAddr );
      if ( (idx < GAP_BONDINGS_MAX) && (pResolvedAddr) )
      {
        VOID gapBondMgrGetPublicAddr( idx, pResolvedAddr );
      }
      break;

    case ADDRTYPE_PRIVATE_RESOLVE:
      // Master's don't use Private Resolvable addresses but just in case
      idx = gapBondMgrResolvePrivateAddr( pDevAddr );
      if ( (idx < GAP_BONDINGS_MAX) && (pResolvedAddr) )
      {
        VOID gapBondMgrGetPublicAddr( idx, pResolvedAddr );
      }
      break;

    default:
      break;
  }

  return ( idx );
}

/*********************************************************************
 * @brief   Set/clear the service change indication in a bond record.
 *
 * Public function defined in gapbondmgr.h.
 */
bStatus_t GAPBondMgr_ServiceChangeInd( uint16 connectionHandle, uint8 setParam )
{
  bStatus_t ret = bleNoResources; // return value

  if ( connectionHandle == 0xFFFF )
  {
    uint8 idx;  // loop counter

    // Run through the bond database and update the Service Change indication
    for ( idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
    {
      if ( gapBondMgrChangeState( idx, GAP_BONDED_STATE_SERVICE_CHANGED, setParam ) )
      {
        ret = SUCCESS;
      }
    }

    // If the service change indication is TRUE, tell the connected devices
    if ( setParam )
    {
      // Run connected database
      linkDB_PerformFunc( gapBondMgrSendServiceChange );
    }
  }
  else
  {
    // Find connection information
    linkDBItem_t *pLinkItem = linkDB_Find( connectionHandle );
    if ( pLinkItem )
    {
      uint8 idx; // loop counter
      idx = GAPBondMgr_ResolveAddr( pLinkItem->addrType, pLinkItem->addr, NULL );
      if ( idx < GAP_BONDINGS_MAX )
      {
        // Bond found, update it.
        VOID gapBondMgrChangeState( idx, GAP_BONDED_STATE_SERVICE_CHANGED, setParam );
        ret = SUCCESS;
      }

      // If the service change indication is TRUE, tell the connected device
      if ( setParam )
      {
        gapBondMgrSendServiceChange( pLinkItem );
      }
    }
    else
    {
      ret = bleNotConnected;
    }
  }

  return ( ret );
}

/*********************************************************************
 * @brief   Update the Characteristic Configuration in a bond record.
 *
 * Public function defined in gapbondmgr.h.
 */
bStatus_t GAPBondMgr_UpdateCharCfg( uint16 connectionHandle, uint16 attrHandle, uint16 value )
{
  bStatus_t ret = bleNoResources; // return value

  if ( connectionHandle == INVALID_CONNHANDLE )
  {
    uint8 idx;  // loop counter

    // Run through the bond database and update the Characteristic Configuration
    for ( idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
    {
      if ( gapBondMgrUpdateCharCfg( idx, attrHandle, value ) )
      {
        ret = SUCCESS;
      }
    }
  }
  else
  {
    // Find connection information
    linkDBItem_t *pLinkItem = linkDB_Find( connectionHandle );
    if ( pLinkItem )
    {
      uint8 idx = GAPBondMgr_ResolveAddr( pLinkItem->addrType, pLinkItem->addr, NULL );
      if ( idx < GAP_BONDINGS_MAX )
      {
        // Bond found, update it.
        VOID gapBondMgrUpdateCharCfg( idx, attrHandle, value );
        ret = SUCCESS;
      }
    }
    else
    {
      ret = bleNotConnected;
    }
  }

  return ( ret );
}

/*********************************************************************
 * @brief   Register callback functions with the bond manager.
 *
 * Public function defined in gapbondmgr.h.
 */
void GAPBondMgr_Register( gapBondCBs_t *pCB )
{
  pGapBondCB = pCB;

  if(gapBondMgr_TaskID != INVALID_TASK_ID)
  {
    // Take over the processing of Authentication messages
    VOID GAP_SetParamValue( TGAP_AUTH_TASK_ID, gapBondMgr_TaskID );

    // Register with GATT Server App for event messages
    GATTServApp_RegisterForMsg( gapBondMgr_TaskID );
  }
}

/*********************************************************************
 * @brief   Respond to a passcode request.
 *
 * Public function defined in gapbondmgr.h.
 */
bStatus_t GAPBondMgr_PasscodeRsp( uint16 connectionHandle, uint8 status, uint32 passcode )
{
  bStatus_t ret = SUCCESS;

  if ( status == SUCCESS )
  {
    // Truncate the passcode
    passcode = passcode % (GAP_PASSCODE_MAX + 1);

    ret = GAP_PasscodeUpdate( passcode, connectionHandle );
    if ( ret != SUCCESS )
    {
      VOID GAP_TerminateAuth( connectionHandle, SMP_PAIRING_FAILED_PASSKEY_ENTRY_FAILED );
    }
  }
  else
  {
    VOID GAP_TerminateAuth( connectionHandle, status );
  }

  return ret;
}

/*********************************************************************
 * @brief   This is a bypass mechanism to allow the bond manager to process
 *              GAP messages.
 *
 * Public function defined in gapbondmgr.h.
 */
uint8 GAPBondMgr_ProcessGAPMsg( gapEventHdr_t *pMsg )
{
  switch ( pMsg->opcode )
  {
    case GAP_PASSKEY_NEEDED_EVENT:
      {
        gapPasskeyNeededEvent_t *pPkt = (gapPasskeyNeededEvent_t *)pMsg;

        if ( pGapBondCB && pGapBondCB->passcodeCB )
        {
          // Ask app for a passcode
          pGapBondCB->passcodeCB( pPkt->deviceAddr, pPkt->connectionHandle, pPkt->uiInputs, pPkt->uiOutputs );
        }
        else
        {
          // No app support, use the default passcode
          if ( GAP_PasscodeUpdate( gapBond_Passcode, pPkt->connectionHandle ) != SUCCESS )
          {
            VOID GAP_TerminateAuth( pPkt->connectionHandle, SMP_PAIRING_FAILED_PASSKEY_ENTRY_FAILED );
          }
        }
      }
      break;

    case GAP_AUTHENTICATION_COMPLETE_EVENT:
      {
        gapAuthCompleteEvent_t *pPkt = (gapAuthCompleteEvent_t *)pMsg;

        // Should we save bonding information (one save at a time)
        if ( (pPkt->hdr.status == SUCCESS)             && 
             (pPkt->authState & SM_AUTH_STATE_BONDING) &&
             (pAuthEvt[pPkt->connectionHandle] == NULL) )
        {
          gapBondRec_t bondRec;

          VOID osal_memset( &bondRec, 0, sizeof ( gapBondRec_t ) ) ;

          // Do we have a public address in the data?
          if ( pPkt->pIdentityInfo )
          {
            VOID osal_memcpy( bondRec.publicAddr, pPkt->pIdentityInfo->bd_addr, B_ADDR_LEN );
          }
          else
          {
            linkDBItem_t *pLinkItem = linkDB_Find( pPkt->connectionHandle );
            if ( pLinkItem )
            {
              VOID osal_memcpy( bondRec.publicAddr, pLinkItem->addr, B_ADDR_LEN );
            }
            else
            {
              // We don't have an address, so ignore the message.
              break;
            }
          }

          // Save off of the authentication state
          bondRec.stateFlags |= (pPkt->authState & SM_AUTH_STATE_AUTHENTICATED) ? GAP_BONDED_STATE_AUTHENTICATED : 0;

          if ( !gapBondMgrAddBond( &bondRec, pPkt ) )
          {
            // Notify our task to save bonding information in NV
            // osal_set_event( gapBondMgr_TaskID, GAP_BOND_SAVE_REC_EVT );
            gapBondRecvEvt( pPkt->connectionHandle,&bondRec, pPkt );
            // We're not done with this message; it will be freed later
            return ( FALSE );
          }
        }

        // Call app state callback in the fail case. Success is handled after GAP_BOND_SAVE_REC_EVT.
        if ( pGapBondCB && pGapBondCB->pairStateCB )
        {
          pGapBondCB->pairStateCB( pPkt->connectionHandle, GAPBOND_PAIRING_STATE_COMPLETE, pPkt->hdr.status );
        }
      }
      break;

    case GAP_BOND_COMPLETE_EVENT:
      // This message is received when the bonding is complete.  If hdr.status is SUCCESS
      // then call app state callback.  If hdr.status is NOT SUCCESS, the connection will be
      // dropped at the LL because of a MIC failure, so again nothing to do.
      {
        gapBondCompleteEvent_t *pPkt = (gapBondCompleteEvent_t *)pMsg;

#if ( HOST_CONFIG & CENTRAL_CFG )
        if ( pPkt->hdr.status == LL_ENC_KEY_REQ_REJECTED )
        {
          // LTK not found on peripheral device (Pin or Key Missing). See which
          // option was configured for unsuccessful bonding.
          linkDBItem_t *pLinkItem = linkDB_Find( pPkt->connectionHandle );
          if ( pLinkItem )
          {
            switch ( gapBond_BondFailOption )
            {
              case GAPBOND_FAIL_INITIATE_PAIRING:
                // Initiate pairing
                gapBondMgrAuthenticate( pPkt->connectionHandle, pLinkItem->addrType, NULL );
                break;

              case GAPBOND_FAIL_TERMINATE_LINK:
                // Drop connection
                GAP_TerminateLinkReq( pLinkItem->taskID, pPkt->connectionHandle, HCI_DISCONNECT_AUTH_FAILURE );
                break;

              case GAPBOND_FAIL_TERMINATE_ERASE_BONDS:
                // Set up bond manager to erase all existing bonds after connection terminates
                VOID GAPBondMgr_SetParameter( GAPBOND_ERASE_ALLBONDS, 0, NULL );

                // Drop connection
                GAP_TerminateLinkReq( pLinkItem->taskID, pPkt->connectionHandle, HCI_DISCONNECT_AUTH_FAILURE );
                break;

              case GAPBOND_FAIL_NO_ACTION:
                // fall through
              default:
                // do nothing
                break;
            }
          }
        }
#endif
        if ( pGapBondCB && pGapBondCB->pairStateCB )
        {
          pGapBondCB->pairStateCB( pPkt->connectionHandle, GAPBOND_PAIRING_STATE_BONDED, pMsg->hdr.status );
        }
      }
      break;

    case GAP_SIGNATURE_UPDATED_EVENT:
      {
        uint8 idx;
        gapSignUpdateEvent_t *pPkt = (gapSignUpdateEvent_t *)pMsg;

        idx = GAPBondMgr_ResolveAddr( pPkt->addrType, pPkt->devAddr, NULL );
        if ( idx < GAP_BONDINGS_MAX )
        {
          // Save the sign counter
          VOID osal_snv_write( devSignCounterNvID(idx), sizeof ( uint32 ), &(pPkt->signCounter) );
        }
      }
      break;

#if ( HOST_CONFIG & PERIPHERAL_CFG )
    case GAP_PAIRING_REQ_EVENT:
      {
        gapPairingReqEvent_t *pPkt = (gapPairingReqEvent_t *)pMsg;

        if ( gapBond_AutoFail != FALSE )
        {
          // Auto Fail TEST MODE (DON'T USE THIS) - Sends pre-setup reason
          VOID GAP_TerminateAuth( pPkt->connectionHandle, gapBond_AutoFailReason );
        }
        else if ( gapBond_PairingMode[pPkt->connectionHandle] == GAPBOND_PAIRING_MODE_NO_PAIRING )
        {
          // No Pairing - Send error
          VOID GAP_TerminateAuth( pPkt->connectionHandle, SMP_PAIRING_FAILED_NOT_SUPPORTED );
        }
        else
        {
          linkDBItem_t *pLinkItem = linkDB_Find( pPkt->connectionHandle );

          // Requesting bonding?
          if ( pPkt->pairReq.authReq & SM_AUTH_STATE_BONDING )
          {
            if ( pLinkItem )
            {
              if ( (pLinkItem->addrType != ADDRTYPE_PUBLIC) && (pPkt->pairReq.keyDist.mIdKey == FALSE) )
              {
                uint8 publicAddr[B_ADDR_LEN];

                // Check if we already have the public address in NV
                if ( GAPBondMgr_ResolveAddr(pLinkItem->addrType, pLinkItem->addr, publicAddr ) == FALSE )
                {
                  // Can't bond to a non-public address if we don't know the public address
                  VOID GAP_TerminateAuth( pPkt->connectionHandle, SMP_PAIRING_FAILED_AUTH_REQ );
                  break;
                }
              }
            }
            else
            {
              // Can't find the connection, ignore the message
              break;
            }
          }

          // Send pairing response
          gapBondMgrAuthenticate( pPkt->connectionHandle, pLinkItem->addrType, &(pPkt->pairReq) );

          // Call app state callback
          if ( pGapBondCB && pGapBondCB->pairStateCB )
          {
            pGapBondCB->pairStateCB( pPkt->connectionHandle, GAPBOND_PAIRING_STATE_STARTED, SUCCESS );
          }
        }
      }
      break;
#endif

#if ( HOST_CONFIG & CENTRAL_CFG )
    case GAP_SLAVE_REQUESTED_SECURITY_EVENT:
      {
        uint16 connHandle = ((gapSlaveSecurityReqEvent_t *)pMsg)->connectionHandle;
        uint8 idx;
        uint8 publicAddr[B_ADDR_LEN] = {0, 0, 0, 0, 0, 0};
        linkDBItem_t *pLink = linkDB_Find( connHandle );

        // If link found and not already initiating security
        if (pLink != NULL && gapBond_PairingMode[connHandle] != GAPBOND_PAIRING_MODE_INITIATE)
        {
          // If already bonded initiate encryption
          idx = GAPBondMgr_ResolveAddr( pLink->addrType, pLink->addr, publicAddr );
          if ( idx < GAP_BONDINGS_MAX )
          {
            gapBondMgrBondReq( connHandle, idx, gapBondMgrGetStateFlags( idx ),
                               GAP_PROFILE_CENTRAL, TRUE );
          }
          // Else if no pairing allowed
          else if ( gapBond_PairingMode[connHandle] == GAPBOND_PAIRING_MODE_NO_PAIRING )
          {
            // Send error
            VOID GAP_TerminateAuth( connHandle, SMP_PAIRING_FAILED_NOT_SUPPORTED );
          }
          // Else if waiting for request
          else if (gapBond_PairingMode[connHandle] == GAPBOND_PAIRING_MODE_WAIT_FOR_REQ)
          {
            // Initiate pairing
            gapBondMgrAuthenticate( connHandle, pLink->addrType, NULL );
          }
        }
      }
      break;
#endif

    case GAP_LINK_TERMINATED_EVENT:
      if ( GAP_NumActiveConnections() == 0 )
      {
        // See if we're asked to erase all bonding records
        if ( eraseAllBonds == TRUE )
        {
          VOID gapBondMgrEraseAllBondings();
          eraseAllBonds = FALSE;
          
          // Reset bonds to delete table
          osal_memset( bondsToDelete, FALSE, sizeof( bondsToDelete ) );
        }
        else
        {
          // See if we're asked to erase any single bonding records
          for (uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++)
          {
            if ( bondsToDelete[idx] == TRUE )
            {
              VOID gapBondMgrEraseBonding( idx );
              bondsToDelete[idx] = FALSE;
            }
          }
        }

        // See if NV needs a compaction
        VOID osal_snv_compact( NV_COMPACT_THRESHOLD );

        // Make sure Bond RAM Shadow is up-to-date
        gapBondMgrReadBonds();
      }
      break;

    default:
      break;
  }
  
  return ( TRUE );
}

/*********************************************************************
 * LOCAL FUNCTION PROTOTYPES
 */

/*********************************************************************
 * @fn      gapBondMgrChangeState
 *
 * @brief   Change a state flag in the stateFlags field of the bond record.
 *
 * @param   idx - Bond NV index
 * @param   state - state flage to set or clear
 * @param   set - TRUE to set the flag, FALSE to clear the flag
 *
 * @return  TRUE if NV Record exists, FALSE if NV Record is empty
 */
static uint8 gapBondMgrChangeState( uint8 idx, uint16 state, uint8 set )
{
  gapBondRec_t bondRec;   // Space to read a Bond record from NV

  // Look for public address that is used (not all 0xFF's)
  if ( (osal_snv_read( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec ) == SUCCESS)
      && (osal_isbufset( bondRec.publicAddr, 0xFF, B_ADDR_LEN ) == FALSE) )
  {
    // Update the state of the bonded device.
    uint8 stateFlags = bondRec.stateFlags;
    if ( set )
    {
      stateFlags |= state;
    }
    else
    {
      stateFlags &= ~(state);
    }

    if ( stateFlags != bondRec.stateFlags )
    {
      bondRec.stateFlags = stateFlags;
      VOID osal_snv_write( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec );
    }
    return ( TRUE );
  }
  return ( FALSE );
}

/*********************************************************************
 * @fn      gapBondMgrUpdateCharCfg
 *
 * @brief   Update the Characteristic Configuration of the bond record.
 *
 * @param   idx - Bond NV index
 * @param   attrHandle - attribute handle (0 means all handles)
 * @param   value - characteristic configuration value
 *
 * @return  TRUE if NV Record exists, FALSE if NV Record is empty
 */
static uint8 gapBondMgrUpdateCharCfg( uint8 idx, uint16 attrHandle, uint16 value )
{
  gapBondRec_t bondRec;   // Space to read a Bond record from NV

  // Look for public address that is used (not all 0xFF's)
  if ( ( osal_snv_read( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec ) == SUCCESS )
       && ( osal_isbufset( bondRec.publicAddr, 0xFF, B_ADDR_LEN ) == FALSE ) )
  {
    gapBondCharCfg_t charCfg[GAP_CHAR_CFG_MAX]; // Space to read a char cfg record from NV

    if ( osal_snv_read( gattCfgNvID(idx), sizeof ( charCfg ), charCfg ) == SUCCESS )
    {
      uint8 update = FALSE;

      gapBondMgrInvertCharCfgItem( charCfg );

      if ( attrHandle == GATT_INVALID_HANDLE )
      {
        if ( osal_isbufset( (uint8 *)charCfg, 0x00, sizeof ( charCfg ) ) == FALSE )
        {
          // Clear all characteristic configuration for this device
          VOID osal_memset( (void *)charCfg, 0x00, sizeof ( charCfg ) );
          update = TRUE;
        }
      }
      else
      {
        gapBondCharCfg_t *pItem = gapBondMgrFindCharCfgItem( attrHandle, charCfg );
        if ( pItem == NULL )
        {
          // Must be a new item; ignore if the value is no operation (default)
          if ( ( value == GATT_CFG_NO_OPERATION ) ||
               ( ( pItem = gapBondMgrFindCharCfgItem( GATT_INVALID_HANDLE, charCfg ) ) == NULL ) )
          {
            return ( FALSE ); // No empty entry found
          }

          pItem->attrHandle = attrHandle;
        }

        if ( pItem->value != value )
        {
          // Update characteristic configuration
          pItem->value = (uint8)value;
          if ( value == GATT_CFG_NO_OPERATION )
          {
            // Erease the item
            pItem->attrHandle = GATT_INVALID_HANDLE;
          }

          update = TRUE;
        }
      }

      // Update the characteristic configuration of the bonded device.
      if ( update )
      {
        gapBondMgrInvertCharCfgItem( charCfg );
        VOID osal_snv_write( gattCfgNvID(idx), sizeof( charCfg ), charCfg );
      }
    }

    return ( TRUE );
  }

  return ( FALSE );
}

/*********************************************************************
 * @fn      gapBondMgrFindCharCfgItem
 *
 * @brief   Find the Characteristic Configuration for a given attribute.
 *          Uses the attribute handle to search the charactersitic
 *          configuration table of a bonded device.
 *
 * @param   attrHandle - attribute handle.
 * @param   charCfgTbl - characteristic configuration table.
 *
 * @return  pointer to the found item. NULL, otherwise.
 */
static gapBondCharCfg_t *gapBondMgrFindCharCfgItem( uint16 attrHandle,
                                                    gapBondCharCfg_t *charCfgTbl )
{
  for ( uint8 i = 0; i < GAP_CHAR_CFG_MAX; i++ )
  {
    if ( charCfgTbl[i].attrHandle == attrHandle )
    {
      return ( &(charCfgTbl[i]) );
    }
  }

  return ( (gapBondCharCfg_t *)NULL );
}

/*********************************************************************
 * @fn      gapBondMgrFindCharCfgItem
 *
 * @brief   Invert the Characteristic Configuration for a given client.
 *
 * @param   charCfgTbl - characteristic configuration table.
 *
 * @return  none.
 */
static void gapBondMgrInvertCharCfgItem( gapBondCharCfg_t *charCfgTbl )
{
  for ( uint8 i = 0; i < GAP_CHAR_CFG_MAX; i++ )
  {
    charCfgTbl[i].attrHandle = ~(charCfgTbl[i].attrHandle);
    charCfgTbl[i].value = ~(charCfgTbl[i].value);
  }
}

/*********************************************************************
 * @fn      gapBondMgrAddBond
 *
 * @brief   Save a bond from a GAP Auth Complete Event
 *
 * @param   pBondRec - basic bond record
 * @param   pLocalLTK - LTK used by this device during pairing
 * @param   pDevLTK - LTK used by the connected device during pairing
 * @param   pIRK - IRK used by the connected device during pairing
 * @param   pSRK - SRK used by the connected device during pairing
 * @param   signCounter - Sign counter used by the connected device during pairing
 *
 * @return  TRUE, if done processing bond record. FALSE, otherwise.
 */
static uint8 gapBondMgrAddBond( gapBondRec_t *pBondRec, gapAuthCompleteEvent_t *pPkt )
{ 
  // See if this is a new bond record
  if ( pAuthEvt[pPkt->connectionHandle] == NULL )
  {
    // Make sure we have bonding info
    if ( ( pBondRec == NULL ) || ( pPkt == NULL ) )
    {
      return ( TRUE );
    }

    // First see if we already have an existing bond for this device
    bondIdx = gapBondMgrFindAddr( pBondRec->publicAddr );
    if ( bondIdx >= GAP_BONDINGS_MAX )
    {
      bondIdx = gapBondMgrFindEmpty();
    }
        #if(GAP_BOND_MGR_INDEX_REPLACE)

        /*replace bondIdx*/
        if(bondIdx==GAP_BONDINGS_MAX)
        {
            bondIdx = (bondReplaceCnt++)%GAP_BONDINGS_MAX;
            BOND_LOG("replace BondIdx %d \n",bondIdx);
        }

        #endif
        BOND_LOG("BondMgrAdd idx=%03d\n",bondIdx);
    }

    if ( bondIdx < GAP_BONDINGS_MAX )
    {
        // See if this is a new bond record
        if ( pAuthEvt[pPkt->connectionHandle] == NULL )
        {
            gapBondCharCfg_t charCfg[GAP_CHAR_CFG_MAX];
            // Save the main information
            osal_snv_write( mainRecordNvID(bondIdx), sizeof ( gapBondRec_t ), pBondRec );
            // Write out FF's over the charactersitic configuration entry, to overwrite
            // any previous bond data that may have been stored
            VOID osal_memset( charCfg, 0xFF, sizeof ( charCfg ) );
            VOID osal_snv_write( gattCfgNvID(bondIdx), sizeof ( charCfg ), charCfg );
            // Update Bond RAM Shadow just with the newly added bond entry
            VOID osal_memcpy( &(bonds[bondIdx]), pBondRec, sizeof ( gapBondRec_t ) );
            // Keep the OSAL message to store the security keys later - will be freed then
            pAuthEvt[pPkt->connectionHandle] = pPkt;
        }
        else
        {
            // If available, save the LTK information
            if ( pAuthEvt[pPkt->connectionHandle]->pSecurityInfo )
            {
                VOID osal_snv_write( localLTKNvID(bondIdx), sizeof ( gapBondLTK_t ), pAuthEvt[pPkt->connectionHandle]->pSecurityInfo );
                pAuthEvt[pPkt->connectionHandle]->pSecurityInfo = NULL;
            }
            // If availabe, save the connected device's LTK information
            if ( pAuthEvt[pPkt->connectionHandle]->pDevSecInfo )
            {
                VOID osal_snv_write( devLTKNvID(bondIdx), sizeof ( gapBondLTK_t ), pAuthEvt[pPkt->connectionHandle]->pDevSecInfo );
                pAuthEvt[pPkt->connectionHandle]->pDevSecInfo = NULL;
            }
            // If available, save the connected device's IRK
            if ( pAuthEvt[pPkt->connectionHandle]->pIdentityInfo )
            {
                VOID osal_snv_write( devIRKNvID(bondIdx), KEYLEN, pAuthEvt[pPkt->connectionHandle]->pIdentityInfo->irk );
                pAuthEvt[pPkt->connectionHandle]->pIdentityInfo = NULL;
            }
            // If available, save the connected device's Signature information
            if ( pAuthEvt[pPkt->connectionHandle]->pSigningInfo )
            {
                VOID osal_snv_write( devCSRKNvID(bondIdx), KEYLEN, pAuthEvt[pPkt->connectionHandle]->pSigningInfo->srk );
                VOID osal_snv_write( devSignCounterNvID(bondIdx), sizeof ( uint32 ), &(pAuthEvt[pPkt->connectionHandle]->pSigningInfo->signCounter) );
                pAuthEvt[pPkt->connectionHandle]->pSigningInfo = NULL;
            }
            // else
            {
                if ( autoSyncWhiteList )
                {
                    gapBondMgr_SyncWhiteList();
                }

        // Update the GAP Privacy Flag Properties
        gapBondSetupPrivFlag();
        
        return ( TRUE );
      }
    }
    
    // We have more info to store
    return ( FALSE );
  }

  return ( TRUE );
}

/*********************************************************************
 * @fn      gapBondMgrGetStateFlags
 *
 * @brief   Gets the state flags field of a bond record in NV
 *
 * @param   idx
 *
 * @return  stateFlags field
 */
static uint8 gapBondMgrGetStateFlags( uint8 idx )
{
  gapBondRec_t bondRec;

  if ( osal_snv_read( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec ) == SUCCESS )
  {
    return ( bondRec.stateFlags );
  }

  return ( 0 );
}

/*********************************************************************
 * @fn      gapBondMgrGetPublicAddr
 *
 * @brief   Copy the public Address from a bonding record
 *
 * @param   idx - Bond record index
 * @param   pAddr - a place to put the public address from NV
 *
 * @return  SUCCESS if successful.
 *          Otherwise failure.
 */
static bStatus_t gapBondMgrGetPublicAddr( uint8 idx, uint8 *pAddr )
{
  bStatus_t stat;         // return value
  gapBondRec_t bondRec;   // Work space for main bond record

  // Check parameters
  if ( (idx >= GAP_BONDINGS_MAX) || (pAddr == NULL) )
  {
    return ( INVALIDPARAMETER );
  }

  stat = osal_snv_read( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec );

  if ( stat == SUCCESS )
  {
    VOID osal_memcpy( pAddr, bondRec.publicAddr, B_ADDR_LEN );
  }

  return ( stat );
}

/*********************************************************************
 * @fn      gapBondMgrFindReconnectAddr
 *
 * @brief   Look through the bonding entries to find a
 *          reconnection address.
 *
 * @param   pReconnectAddr - device address to look for
 *
 * @return  index to found bonding (0 - (GAP_BONDINGS_MAX-1),
 *          GAP_BONDINGS_MAX if no empty entries
 */
static uint8 gapBondMgrFindReconnectAddr( uint8 *pReconnectAddr )
{
  // Item doesn't exist, so create all the items
  for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
  {
    // compare reconnection address
    if ( osal_memcmp( bonds[idx].reconnectAddr, pReconnectAddr, B_ADDR_LEN ) )
    {
      return ( idx ); // Found it
    }
  }

  return ( GAP_BONDINGS_MAX );
}

/*********************************************************************
 * @fn      gapBondMgrFindAddr
 *
 * @brief   Look through the bonding entries to find an address.
 *
 * @param   pDevAddr - device address to look for
 *
 * @return  index to empty bonding (0 - (GAP_BONDINGS_MAX-1),
 *          GAP_BONDINGS_MAX if no empty entries
 */
static uint8 gapBondMgrFindAddr( uint8 *pDevAddr )
{
  // Item doesn't exist, so create all the items
  for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
  {
    // Read in NV Main Bond Record and compare public address
    if ( osal_memcmp( bonds[idx].publicAddr, pDevAddr, B_ADDR_LEN ) )
    {
      return ( idx ); // Found it
    }
  }

  return ( GAP_BONDINGS_MAX );
}

/*********************************************************************
 * @fn      gapBondMgrResolvePrivateAddr
 *
 * @brief   Look through the NV bonding entries to resolve a private
 *          address.
 *
 * @param   pDevAddr - device address to look for
 *
 * @return  index to found bonding (0 - (GAP_BONDINGS_MAX-1),
 *          GAP_BONDINGS_MAX if no entry found
 */
static uint8 gapBondMgrResolvePrivateAddr( uint8 *pDevAddr )
{
  for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
  {
    uint8 IRK[KEYLEN];

    // Read in NV IRK Record and compare resovable address
    if ( osal_snv_read( devIRKNvID(idx), KEYLEN, IRK ) == SUCCESS )
    {
      if ( ( osal_isbufset( IRK, 0xFF, KEYLEN ) == FALSE ) &&
           ( GAP_ResolvePrivateAddr( IRK, pDevAddr ) == SUCCESS ) )
      {
        return ( idx ); // Found it
      }
    }
  }

  return ( GAP_BONDINGS_MAX );
}

/*********************************************************************
 * @fn      gapBondMgrReadBonds
 *
 * @brief   Read through NV and store them in RAM.
 *
 * @param   none
 *
 * @return  none
 */
static void gapBondMgrReadBonds( void )
{
  for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
  {
    // See if the entry exists in NV
    if ( osal_snv_read( mainRecordNvID(idx), sizeof( gapBondRec_t ), &(bonds[idx]) ) != SUCCESS )
    {
      // Can't read the entry, assume that it doesn't exist
      VOID osal_memset( bonds[idx].publicAddr, 0xFF, B_ADDR_LEN );
      VOID osal_memset( bonds[idx].reconnectAddr, 0xFF, B_ADDR_LEN );
      bonds[idx].stateFlags = 0;
    }
  }

  if ( autoSyncWhiteList )
  {
    gapBondMgr_SyncWhiteList();
  }

  // Update the GAP Privacy Flag Properties
  gapBondSetupPrivFlag();
}

/*********************************************************************
 * @fn      gapBondMgrFindEmpty
 *
 * @brief   Look through the bonding NV entries to find an empty.
 *
 * @param   none
 *
 * @return  index to empty bonding (0 - (GAP_BONDINGS_MAX-1),
 *          GAP_BONDINGS_MAX if no empty entries
 */
static uint8 gapBondMgrFindEmpty( void )
{
  // Item doesn't exist, so create all the items
  for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
  {
    // Look for public address of all 0xFF's
    if ( osal_isbufset( bonds[idx].publicAddr, 0xFF, B_ADDR_LEN ) )
    {
      return ( idx ); // Found one
    }
  }

  return ( GAP_BONDINGS_MAX );
}

/*********************************************************************
 * @fn      gapBondMgrBondTotal
 *
 * @brief   Look through the bonding NV entries calculate the number
 *          entries.
 *
 * @param   none
 *
 * @return  total number of bonds found
 */
static uint8 gapBondMgrBondTotal( void )
{
  uint8 numBonds = 0;

  // Item doesn't exist, so create all the items
  for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ )
  {
    // Look for public address that are not 0xFF's
    if ( osal_isbufset( bonds[idx].publicAddr, 0xFF, B_ADDR_LEN ) == FALSE )
    {
      numBonds++; // Found one
    }
  }

  return ( numBonds );
}

/*********************************************************************
 * @fn      gapBondMgrEraseAllBondings
 *
 * @brief   Write all 0xFF's to all of the bonding entries
 *
 * @param   none
 *
 * @return  SUCCESS if successful.
 *          Otherwise, NV_OPER_FAILED for failure.
 */
static bStatus_t gapBondMgrEraseAllBondings( void )
{
  bStatus_t stat = SUCCESS;  // return value

  // Item doesn't exist, so create all the items
  for ( uint8 idx = 0; (idx < GAP_BONDINGS_MAX) && (stat == SUCCESS); idx++ )
  {
    // Erasing will write/create a bonding entry
    stat = gapBondMgrEraseBonding( idx );
  }

  return ( stat );
}

/*********************************************************************
 * @fn      gapBondMgrEraseBonding
 *
 * @brief   Write all 0xFF's to the complete bonding record
 *
 * @param   idx - bonding index
 *
 * @return  SUCCESS if successful.
 *          Otherwise, NV_OPER_FAILED for failure.
 */
static bStatus_t gapBondMgrEraseBonding( uint8 idx )
{
  bStatus_t ret;
  gapBondRec_t bondRec;

  if ( idx == bondIdx )
  {
    // Stop ongoing bond store process to prevent any invalid data be written.
    // osal_clear_event( gapBondMgr_TaskID, GAP_BOND_SYNC_CC_EVT );
    // osal_clear_event( gapBondMgr_TaskID, GAP_BOND_SAVE_REC_EVT );

    // gapBondFreeAuthEvt();
  }
  
  // First see if bonding record exists in NV, then write all 0xFF's to it
  if ( ( osal_snv_read( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec ) == SUCCESS )
       && (osal_isbufset( bondRec.publicAddr, 0xFF, B_ADDR_LEN ) == FALSE) )
  {
    gapBondLTK_t ltk;
    gapBondCharCfg_t charCfg[GAP_CHAR_CFG_MAX];

    VOID osal_memset( &bondRec, 0xFF, sizeof ( gapBondRec_t ) );
    VOID osal_memset( &ltk, 0xFF, sizeof ( gapBondLTK_t ) );

    VOID osal_memset( charCfg, 0xFF, sizeof ( charCfg ) );

    // Write out FF's over the entire bond entry.
    ret = osal_snv_write( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec );
    ret |= osal_snv_write( localLTKNvID(idx), sizeof ( gapBondLTK_t ), &ltk );
    ret |= osal_snv_write( devLTKNvID(idx), sizeof ( gapBondLTK_t ), &ltk );
    ret |= osal_snv_write( devIRKNvID(idx), KEYLEN, ltk.LTK );
    ret |= osal_snv_write( devCSRKNvID(idx), KEYLEN, ltk.LTK );
    ret |= osal_snv_write( devSignCounterNvID(idx), sizeof ( uint32 ), ltk.LTK );

    // Write out FF's over the charactersitic configuration entry.
    ret |= osal_snv_write( gattCfgNvID(idx), sizeof ( charCfg ), charCfg );
  }
  else
  {
    ret = SUCCESS;
  }

  return ( ret );
}

/*********************************************************************
 * @brief   Task Initialization function.
 *
 * Internal function defined in gapbondmgr.h.
 */
void GAPBondMgr_Init( uint8 task_id )
{
  gapBondMgr_TaskID = task_id;  // Save task ID

  // Setup Bond RAM Shadow
  gapBondMgrReadBonds();
}

/*********************************************************************
 * @brief   Task Event Processor function.
 *
 * Internal function defined in gapbondmgr.h.
 */
uint16 GAPBondMgr_ProcessEvent( uint8 task_id, uint16 events )
{
  VOID task_id; // OSAL required parameter that isn't used in this function

  if ( events & SYS_EVENT_MSG )
  {
    uint8 *pMsg;

    if ( (pMsg = osal_msg_receive( gapBondMgr_TaskID )) != NULL )
    {
      if ( gapBondMgr_ProcessOSALMsg( (osal_event_hdr_t *)pMsg ) )
      {
        // Release the OSAL message
        VOID osal_msg_deallocate( pMsg );
      }
    }

    // return unprocessed events
    return (events ^ SYS_EVENT_MSG);
  }

  // if ( events & GAP_BOND_SAVE_REC_EVT )
  // {
  //   // Save bonding record in NV
  //   if ( gapBondMgrAddBond( NULL, NULL ) )
  //   {      
  //     // Notify our task to update NV with CCC values stored in GATT database
  //     osal_set_event( gapBondMgr_TaskID, GAP_BOND_SYNC_CC_EVT );
           
  //     return (events ^ GAP_BOND_SAVE_REC_EVT);
  //   }

  //   return ( GAP_BOND_SAVE_REC_EVT );
  // }
  
  // if ( events & GAP_BOND_SYNC_CC_EVT )
  // {
  //   // Update NV to have same CCC values as GATT database
  //   // Note: pAuthEvt is a global variable used for deferring the storage
  //   if ( gapBondMgr_SyncCharCfg( pAuthEvt->connectionHandle ) )
  //   {      
  //     if ( pGapBondCB && pGapBondCB->pairStateCB )
  //     {
  //       // Assume SUCCESS since we got this far.
  //       pGapBondCB->pairStateCB( pAuthEvt->connectionHandle, GAPBOND_PAIRING_STATE_COMPLETE, SUCCESS );
  //     }
      
  //     // We're done storing bond record and CCC values in NV
  //     gapBondFreeAuthEvt();
    
  //     return (events ^ GAP_BOND_SYNC_CC_EVT);
  //   }
    
  //   return ( GAP_BOND_SYNC_CC_EVT );
  // }

  // Discard unknown events
  return 0;
}

/*********************************************************************
 * @fn      gapBondMgr_ProcessOSALMsg
 *
 * @brief   Process an incoming task message.
 *
 * @param   pMsg - message to process
 *
 * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
 */
static uint8 gapBondMgr_ProcessOSALMsg( osal_event_hdr_t *pMsg )
{
  uint8 safeToDealloc = TRUE;
  
  switch ( pMsg->event )
  {
    case GAP_MSG_EVENT:
      safeToDealloc = GAPBondMgr_ProcessGAPMsg( (gapEventHdr_t *)pMsg );
      break;

    case GATT_MSG_EVENT:
      gapBondMgr_ProcessGATTMsg( (gattMsgEvent_t *)pMsg );
      break;

    case GATT_SERV_MSG_EVENT:
      gapBondMgr_ProcessGATTServMsg( (gattEventHdr_t *)pMsg );
      break;

    default:
      break;
  }
  
  return ( safeToDealloc );
}

/*********************************************************************
 * @fn      GAPBondMgr_CheckNVLen
 *
 * @brief   This function will check the length of an NV Item.
 *
 * @param   id - NV ID.
 * @param   len - lengths in bytes of item.
 *
 * @return  SUCCESS or FAILURE
 */
uint8 GAPBondMgr_CheckNVLen( uint8 id, uint8 len )
{
  uint8 stat = FAILURE;

  // Convert to index
  switch ( (id - BLE_NVID_GAP_BOND_START) % GAP_BOND_REC_IDS )
  {
    case GAP_BOND_REC_ID_OFFSET:
      if ( len == sizeof ( gapBondRec_t ) )
      {
        stat = SUCCESS;
      }
      break;

    case GAP_BOND_LOCAL_LTK_OFFSET:
    case GAP_BOND_DEV_LTK_OFFSET:
      if ( len == sizeof ( gapBondLTK_t ) )
      {
        stat = SUCCESS;
      }
      break;

    case GAP_BOND_DEV_IRK_OFFSET:
    case GAP_BOND_DEV_CSRK_OFFSET:
      if ( len == KEYLEN )
      {
        stat = SUCCESS;
      }
      break;

    case GAP_BOND_DEV_SIGN_COUNTER_OFFSET:
      if ( len == sizeof ( uint32 ) )
      {
        stat = SUCCESS;
      }
      break;

    default:
      break;
  }

  return ( stat );
}

/*********************************************************************
 * @fn          gapBondMgr_ProcessGATTMsg
 *
 * @brief       Process an incoming GATT message.
 *
 * @param       pMsg - pointer to received message
 *
 * @return      none
 */
static void gapBondMgr_ProcessGATTMsg( gattMsgEvent_t *pMsg )
{
  // Process the GATT message
  switch ( pMsg->method )
  {
    case ATT_HANDLE_VALUE_CFM:
      // Clear Service Changed flag for this client
      VOID GAPBondMgr_ServiceChangeInd( pMsg->connHandle, 0x00 );
      break;

    default:
      // Unknown message
      break;
  }
}

/*********************************************************************
 * @fn          gapBondMgr_ProcessGATTServMsg
 *
 * @brief       Process an incoming GATT Server App message.
 *
 * @param       pMsg - pointer to received message
 *
 * @return      none
 */
static void gapBondMgr_ProcessGATTServMsg( gattEventHdr_t *pMsg )
{
  // Process the GATT Server App message
  switch ( pMsg->method )
  {
    case GATT_CLIENT_CHAR_CFG_UPDATED_EVENT:
      {
        gattClientCharCfgUpdatedEvent_t *pEvent = (gattClientCharCfgUpdatedEvent_t *)pMsg;

        VOID GAPBondMgr_UpdateCharCfg( pEvent->connHandle, pEvent->attrHandle, pEvent->value );
      }
      break;

    default:
      // Unknown message
      break;
  }
}

/*********************************************************************
 * @fn      gapBondMgrSendServiceChange
 *
 * @brief   Tell the GATT that a service change is needed.
 *
 * @param   pLinkItem - pointer to connection information
 *
 * @return  none
 */
static void gapBondMgrSendServiceChange( linkDBItem_t *pLinkItem )
{
  VOID GATTServApp_SendServiceChangedInd( pLinkItem->connectionHandle,
                                          gapBondMgr_TaskID );
}

/*********************************************************************
 * @fn      gapBondSetupPrivFlag
 *
 * @brief   Setup the GAP Privacy Flag properties.
 *
 * @param   none
 *
 * @return  none
 */
static void gapBondSetupPrivFlag( void )
{
  uint8 privFlagProp;

  if ( gapBondMgrBondTotal() > 1 )
  {
    privFlagProp = GATT_PROP_READ;
  }
  else
  {
    privFlagProp = GATT_PROP_READ | GATT_PROP_WRITE;
  }

  // Setup the
  VOID GGS_SetParameter( GGS_PERI_PRIVACY_FLAG_PROPS, sizeof ( uint8 ), &privFlagProp );
}

/*********************************************************************
 * @fn      gapBondMgrAuthenticate
 *
 * @brief   Initiate authentication
 *
 * @param   connHandle - connection handle
 * @param   addrType - peer address type
 * @param   pPairReq - Enter these parameters if the Pairing Request was already received.
 *          NULL, if waiting for Pairing Request or if initiating.
 *
 * @return  none
 */
static void gapBondMgrAuthenticate( uint16 connHandle, uint8 addrType,
                                    gapPairingReq_t *pPairReq )
{
  gapAuthParams_t params;

  VOID osal_memset( &params, 0, sizeof ( gapAuthParams_t ) );

  // Setup the pairing parameters
  params.connectionHandle = connHandle;
  params.secReqs.ioCaps = gapBond_IOCap;
  params.secReqs.oobAvailable = gapBond_OOBDataFlag;
  params.secReqs.maxEncKeySize = gapBond_KeySize;

  params.secReqs.keyDist.sEncKey = (gapBond_KeyDistList & GAPBOND_KEYDIST_SENCKEY) ? TRUE : FALSE;
  params.secReqs.keyDist.sIdKey = (gapBond_KeyDistList & GAPBOND_KEYDIST_SIDKEY) ? TRUE : FALSE;
  params.secReqs.keyDist.mEncKey = (gapBond_KeyDistList & GAPBOND_KEYDIST_MENCKEY) ? TRUE : FALSE;
  params.secReqs.keyDist.mIdKey = (gapBond_KeyDistList & GAPBOND_KEYDIST_MIDKEY) ? TRUE : FALSE;
  params.secReqs.keyDist.mSign = (gapBond_KeyDistList & GAPBOND_KEYDIST_MSIGN) ? TRUE : FALSE;
  params.secReqs.keyDist.sSign = (gapBond_KeyDistList & GAPBOND_KEYDIST_SSIGN) ? TRUE : FALSE;

  // Is bond manager setup for OOB data?
  if ( gapBond_OOBDataFlag )
  {
    VOID osal_memcpy( params.secReqs.oob, gapBond_OOBData, KEYLEN );
  }

  if ( gapBond_Bonding && addrType != ADDRTYPE_PUBLIC )
  {
    // Force a slave ID key
    params.secReqs.keyDist.sIdKey = TRUE;
  }

  params.secReqs.authReq |= (gapBond_Bonding) ? SM_AUTH_STATE_BONDING : 0;
  params.secReqs.authReq |= (gapBond_MITM) ? SM_AUTH_STATE_AUTHENTICATED : 0;
  params.secReqs.authReq |= (gapBond_SC) ? SM_AUTH_STATE_SC : 0;
    params.secReqs.authReq |= (gapBond_keyPress) ? SM_AUTH_STATE_KEYPRESS : 0;
    params.secReqs.authReq |= (gapBond_CT2) ? SM_AUTH_STATE_CT2 : 0;
    GAP_Authenticate( &params, pPairReq );
}

#if ( HOST_CONFIG & PERIPHERAL_CFG )
/*********************************************************************
 * @fn      gapBondMgrSlaveSecurityReq
 *
 * @brief   Send a slave security request
 *
 * @param   connHandle - connection handle
 *
 * @return  none
 */
static void gapBondMgrSlaveSecurityReq( uint16 connHandle )
{
  uint8 authReq = 0;

  authReq |= (gapBond_Bonding) ? SM_AUTH_STATE_BONDING : 0;
  authReq |= (gapBond_MITM) ? SM_AUTH_STATE_AUTHENTICATED : 0;
  authReq |= (gapBond_SC) ? SM_AUTH_STATE_SC : 0;
    authReq |= (gapBond_keyPress) ? SM_AUTH_STATE_KEYPRESS : 0;
    authReq |= (gapBond_CT2) ? SM_AUTH_STATE_CT2 : 0;
    GAP_SendSlaveSecurityRequest( connHandle, authReq );
}
#endif

/*********************************************************************
 * @fn      gapBondMgrBondReq
 *
 * @brief   Initiate a GAP bond request
 *
 * @param   connHandle - connection handle
 * @param   idx - NV index of bond entry
 * @param   stateFlags - bond state flags
 * @param   role - master or slave role
 * @param   startEncryption - whether or not to start encryption
 *
 * @return  none
 */
static void gapBondMgrBondReq( uint16 connHandle, uint8 idx, uint8 stateFlags,
                               uint8 role, uint8 startEncryption )
{
  smSecurityInfo_t ltk;
  osalSnvId_t      nvId;

  if ( role == GAP_PROFILE_CENTRAL )
  {
    nvId = devLTKNvID( idx );
  }
  else
  {
    nvId = localLTKNvID( idx );
  }

  // Initialize the NV structures
  VOID osal_memset( &ltk, 0, sizeof ( smSecurityInfo_t ) );

  if ( osal_snv_read( nvId, sizeof ( smSecurityInfo_t ), &ltk ) == SUCCESS )
  {
    if ( (ltk.keySize >= MIN_ENC_KEYSIZE) && (ltk.keySize <= MAX_ENC_KEYSIZE) )
    {
      bStatus_t ret = GAP_Bond( connHandle,
                    ((stateFlags & GAP_BONDED_STATE_AUTHENTICATED) ? TRUE : FALSE),
                    &ltk, startEncryption );
    }
  }
}

/*********************************************************************
 * @fn      gapBondMgr_SyncWhiteList
 *
 * @brief   syncronize the White List with the bonds
 *
 * @param   none
 *
 * @return  none
 */
static void gapBondMgr_SyncWhiteList( void )
{
	//erase the White List
	VOID HCI_LE_ClearWhiteListCmd(LL_WHITELIST_DEV_TYPE_MASTER);

	// Write bond addresses into the White List
	for(uint8 i = 0; i < GAP_BONDINGS_MAX; i++)
	{
		// Make sure empty addresses are not added to the White List
		if(osal_isbufset(bonds[i].publicAddr, 0xFF, B_ADDR_LEN) == FALSE)
		{
			VOID HCI_LE_AddWhiteListCmd(LL_WHITELIST_DEV_TYPE_MASTER, HCI_PUBLIC_DEVICE_ADDRESS, bonds[i].publicAddr);
		}
	}
}

/*********************************************************************
 * @fn          gapBondMgr_SyncCharCfg
 *
 * @brief       Update the Bond Manager to have the same configurations as
 *              the GATT database.
 *
 * @param       connHandle - the current connection handle to find client configurations for
 *
 * @return      TRUE if sync done. FALSE, otherwise.
 */
static uint8 gapBondMgr_SyncCharCfg( uint16 connHandle )
{
  static gattAttribute_t *pAttr = NULL;
  static uint16 service;

  // Only attributes with attribute handles between and including the Starting
  // Handle parameter and the Ending Handle parameter that match the requested
  // attribute type and the attribute value will be returned.

  // All attribute types are effectively compared as 128-bit UUIDs,
  // even if a 16-bit UUID is provided in this request or defined
  // for an attribute.
  if ( pAttr == NULL )
  {
    pAttr = GATT_FindHandleUUID( GATT_MIN_HANDLE, GATT_MAX_HANDLE,
                                 clientCharCfgUUID, ATT_BT_UUID_SIZE, &service );
  }
  
  while ( pAttr != NULL )
  {
    uint16 len;
    uint8 attrVal[ATT_BT_UUID_SIZE];

    // It is not possible to use this request on an attribute that has a value
    // that is longer than 2.
    if ( GATTServApp_ReadAttr( connHandle, pAttr, service, attrVal,
                               &len, 0, ATT_BT_UUID_SIZE ) == SUCCESS )
    {
      uint16 value = BUILD_UINT16(attrVal[0], attrVal[1]);

      if ( value != GATT_CFG_NO_OPERATION )
      {
        // NV must be updated to meet configuration of the database
        VOID GAPBondMgr_UpdateCharCfg( connHandle, pAttr->handle, value );
      }
    }

    // Try to find the next attribute
    pAttr = GATT_FindNextAttr( pAttr, GATT_MAX_HANDLE, service, NULL );
  }
  
  return ( pAttr == NULL );    
}

/*********************************************************************
 * @fn          gapBondFreeAuthEvt
 *
 * @brief       Free GAP Authentication Complete event.
 *
 * @param       none
 *
 * @return      none
 */
static void gapBondFreeAuthEvt( uint16 connHandle )
{
  if ( pAuthEvt[connHandle] != NULL )
  {
    // Release the OSAL message
    VOID osal_msg_deallocate( (uint8 *)pAuthEvt[connHandle] );
    pAuthEvt[connHandle] = NULL;
  }
  
  bondIdx = GAP_BONDINGS_MAX;
}

#endif // ( CENTRAL_CFG | PERIPHERAL_CFG )

/*********************************************************************
*********************************************************************/
